/*
Copyright 2010 Peter Krajnc, SCREENWORKER

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.screenworker.compactmvc.extensions.xmlservice
{
	import com.screenworker.compactmvc.extensions.interfaces.IXMLService;
	import com.screenworker.compactmvc.extensions.vo.XMLServiceItemVO;
	import com.screenworker.compactmvc.interfaces.IModel;
	import com.screenworker.compactmvc.patterns.Controller;
	import com.screenworker.compactmvc.patterns.Model;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.unescapeMultiByte;
	
	import mx.rpc.http.Operation;

	public class XMLService extends Model implements IModel, IXMLService
	{
		private var _dataLoader:URLLoader;
		private var _data:XML;
		private var _queueMap:Vector.<XMLServiceItemVO>;
		private var _currentItem:XMLServiceItemVO;
		
		protected var _UTF8Decoding:Boolean = true;
		
		public function XMLService(name:String, controller:Controller)
		{
			super(name, controller);
			
			_queueMap = new Vector.<XMLServiceItemVO>();
			
			_dataLoader = new URLLoader();
			_dataLoader.addEventListener(ProgressEvent.PROGRESS, loaderProgressHandler, false, 0, true);
			_dataLoader.addEventListener(IOErrorEvent.IO_ERROR, loaderIOErrorHandler, false, 0, true);
			_dataLoader.addEventListener(Event.COMPLETE, loaderCompleteHandler, false, 0, true);
			_dataLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loaderSecurityErrorHandler, false, 0, true);	
		}
		
		public function reset():void
		{		
			_dataLoader.close();
			_dataLoader.removeEventListener(ProgressEvent.PROGRESS, loaderProgressHandler );
			_dataLoader.removeEventListener(IOErrorEvent.IO_ERROR, loaderIOErrorHandler);
			_dataLoader.removeEventListener(Event.COMPLETE, loaderCompleteHandler);
			_dataLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, loaderSecurityErrorHandler);	
			_dataLoader = null;
			_data = null;
		}
		
		override public function removeHandler():void
		{
			reset();
		}
		
		/**
		 * loads a xml file from the given url
		 * @param url
		 * 
		 */		
		protected function load(request:URLRequest, resultHandler:Function=null, faultHandler:Function=null, mockupObject:Object=null):void 
		{
			var vo:XMLServiceItemVO = new XMLServiceItemVO();
			vo.request = request;
			vo.resultHandler = resultHandler;
			vo.faultHandler = faultHandler;
			vo.mockupObject = mockupObject;
			
			addToQueue(vo);
        }
		
		/**
		 * adds a new item to the  queue list
		 * @param item
		 * 
		 */		
		private function addToQueue(item:XMLServiceItemVO):void
		{
			_queueMap.push(item);
			
			if(!_currentItem)
				nextItem();	
		}
		
		/**
		 * remove a queue item and close loading operation
		 * @param itemToRemove
		 * @return 
		 * 
		 */		
		public function removeFromQueue(itemToRemove:XMLServiceItemVO):XMLServiceItemVO
		{
			//remove from queue list
			for (var i:int = 0; i < _queueMap.length; i++)
			{
				var item:XMLServiceItemVO = _queueMap[i];
				if(item == itemToRemove)
				{
					_queueMap.splice(i, 1);			
					break;
				}	
			}	
			
			//if item is current, stop loading
			if(_currentItem == itemToRemove)
			{
				_dataLoader.close();
				_currentItem = null;
				nextItem();
				return itemToRemove;
			}
			
			return itemToRemove;			
		}
		
		/**
		 * starts loading the next queue items xml file 
		 * 
		 */		
		private function nextItem():void
		{
			_currentItem = null;
			
			if(_queueMap.length == 0)
				return;
			
			_currentItem = _queueMap.shift();
			
			try 
			{	
				_dataLoader.load(_currentItem.request);	
			} 
			catch (error:Error) 
			{
				var msg:String = 'Unable to load requested document:' + error.message;
				
				if(_currentItem.faultHandler != null)
					_currentItem.faultHandler(msg);
				
				errorHandler(msg);
				
				nextItem();				
			}		
		}
		
		private function loaderProgressHandler( e:ProgressEvent ):void
		{
			progressHandler(e.bytesLoaded/e.bytesTotal);
		}
		
		private function loaderIOErrorHandler(e:IOErrorEvent):void 
		{
			IOErrorHandler(e.text);
			
			nextItem();
        }
		
		private function loaderSecurityErrorHandler(e:SecurityErrorEvent):void
		{
			securityErrorHandler(e.text);
			
			nextItem();
		}
        
		private function loaderCompleteHandler(e:Event):void 
		{	
			_data = validateResult(e.target.data);
			
			if(_data)
			{
				if(_currentItem.resultHandler != null)
					_currentItem.resultHandler();
				
				completeHandler();
				
				nextItem();
			}			
			else
			{
				var msg:String = 'XML is not valid!';
				
				if(_currentItem.faultHandler != null)
					_currentItem.faultHandler(msg);
				
				errorHandler(msg);
				
				nextItem();
			}
				
		}
		
		protected function validateResult(data:Object):XML
		{
			var xml:XML = new XML( data );
			if(xml == null )
				return null;						
			xml.ignoreWhitespace = true;
			return xml;
			
		}
		
		public function progressHandler(progress:Number):void
		{
			//overriden by subclass
		}
		
		public function IOErrorHandler(message:String):void 
		{
			//overriden by subclass
        }
        
        public function errorHandler(message:String):void 
		{
			//overriden by subclass
        }
        
        public function completeHandler():void 
		{
			//overriden by subclass
		}
		
		public function securityErrorHandler(message:String):void
		{
			//overriden by subclass
		}
		
		public function get data():XML
		{
			return _data;
		}
		
		public function set data(value:XML):void
		{
			_data = value;
		}

		public function get UTF8Decoding():Boolean
		{
			return _UTF8Decoding;
		}

		public function set UTF8Decoding(value:Boolean):void
		{
			_UTF8Decoding = value;
		}

		
	}
}
